//==================================================================
//  Copyright (C) 2006-2007  Davide Pasca
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//==================================================================
/// @file
///
///
/// 
/// @author Davide Pasca
/// 
//==================================================================

#include "stdafx.h"
#include "rend_device_state.h"
#include "rend_device.h"

//==================================================================
///
//==================================================================
#ifdef TARGET_D3D
static DWORD _locmap_Culling[] =
{
	D3DCULL_CW,//		CULL_CW,
	D3DCULL_CCW,// 		CULL_CCW,
	D3DCULL_NONE,//		CULL_OFF,
};
#endif

#ifdef TARGET_D3D
static DWORD _locmap_TestFunc[] =
{
	D3DCMP_NEVER,		// TESTFUNC_NEVER,
	D3DCMP_LESS,		// TESTFUNC_LESS,
	D3DCMP_EQUAL,		// TESTFUNC_EQUAL,
	D3DCMP_LESSEQUAL,	// TESTFUNC_LESSEQUAL,
	D3DCMP_GREATER,		// TESTFUNC_GREATER,
	D3DCMP_NOTEQUAL,	// TESTFUNC_NOTEQUAL,
	D3DCMP_GREATEREQUAL,// TESTFUNC_GREATEREQUAL,
	D3DCMP_ALWAYS,		// TESTFUNC_ALWAYS,
};
#elif defined(TARGET_OPENGL)
static u_int _locmap_TestFunc[] =
{
	GL_NEVER,		// TESTFUNC_NEVER,
	GL_LESS,		// TESTFUNC_LESS,
	GL_EQUAL,		// TESTFUNC_EQUAL,
	GL_LEQUAL,		// TESTFUNC_LESSEQUAL,
	GL_GREATER,		// TESTFUNC_GREATER,
	GL_NOTEQUAL,	// TESTFUNC_NOTEQUAL,
	GL_GEQUAL,		// TESTFUNC_GREATEREQUAL,
	GL_ALWAYS,		// TESTFUNC_ALWAYS,
};
#endif

#ifdef TARGET_D3D
static D3DBLEND _locmap_BlendFactor[] =
{
	D3DBLEND_ZERO,				// BLENDFCT_ZERO,
	D3DBLEND_ONE,				// BLENDFCT_ONE,
	D3DBLEND_SRCCOLOR,			// BLENDFCT_SRC_COLOR,
	D3DBLEND_INVSRCCOLOR,		// BLENDFCT_ONE_MINUS_SRC_COLOR,
	D3DBLEND_SRCALPHA,			// BLENDFCT_SRC_ALPHA,
	D3DBLEND_INVSRCALPHA,		// BLENDFCT_ONE_MINUS_SRC_ALPHA,
	D3DBLEND_DESTALPHA,			// BLENDFCT_DST_ALPHA,
	D3DBLEND_INVDESTALPHA,		// BLENDFCT_ONE_MINUS_DST_ALPHA,
	D3DBLEND_DESTCOLOR,			// BLENDFCT_DST_COLOR,
	D3DBLEND_INVDESTCOLOR,		// BLENDFCT_ONE_MINUS_DST_COLOR,
	D3DBLEND_SRCALPHASAT		// BLENDFCT_SRC_ALPHA_SATURATE
};
#elif defined(TARGET_OPENGL)
static u_int	_locmap_BlendFactor[] =
{
	GL_ZERO,					// BLENDFCT_ZERO,
	GL_ONE,						// BLENDFCT_ONE,
	GL_SRC_COLOR,				// BLENDFCT_SRC_COLOR,
	GL_ONE_MINUS_SRC_COLOR,		// BLENDFCT_ONE_MINUS_SRC_COLOR,
	GL_SRC_ALPHA,				// BLENDFCT_SRC_ALPHA,
	GL_ONE_MINUS_SRC_ALPHA,		// BLENDFCT_ONE_MINUS_SRC_ALPHA,
	GL_DST_ALPHA,				// BLENDFCT_DST_ALPHA,
	GL_ONE_MINUS_DST_ALPHA,		// BLENDFCT_ONE_MINUS_DST_ALPHA,
	GL_DST_COLOR,				// BLENDFCT_DST_COLOR,
	GL_ONE_MINUS_DST_COLOR,		// BLENDFCT_ONE_MINUS_DST_COLOR,
	GL_SRC_ALPHA_SATURATE		// BLENDFCT_SRC_ALPHA_SATURATE
};
#endif

#ifdef TARGET_D3D
	//-----------------------------------------------------------------
static D3DSAMPLERSTATETYPE	_locmap_TexFilterType[] = {
	D3DSAMP_MINFILTER,			// TEXFILTERTYPE_MIN,
	D3DSAMP_MAGFILTER,			// TEXFILTERTYPE_MAX,
	D3DSAMP_MIPFILTER,			// TEXFILTERTYPE_MIP,
};
#elif defined(TARGET_OPENGL)
static u_int	_locmap_TexFilterType[] = {
	GL_TEXTURE_MIN_FILTER,			// TEXFILTERTYPE_MIN,
	GL_TEXTURE_MAG_FILTER,			// TEXFILTERTYPE_MAX,
	GL_TEXTURE_MIN_FILTER,			// TEXFILTERTYPE_MIP,
};
#endif

//-----------------------------------------------------------------
#ifdef TARGET_D3D
static D3DTEXTUREFILTERTYPE	_locmap_TexFilterMode[] = {
	D3DTEXF_POINT,				// TEXFILTERMODE_NEAREST,
	D3DTEXF_LINEAR,				// TEXFILTERMODE_LINEAR,
};
#elif defined(TARGET_OPENGL)
static u_int	_locmap_TexFilterMode[] = {
	GL_NEAREST,					// TEXFILTERMODE_NEAREST,
	GL_LINEAR,					// TEXFILTERMODE_LINEAR,
};
#endif

#ifdef TARGET_D3D
//-----------------------------------------------------------------

#elif defined(TARGET_OPENGL)
static u_int	_locmap_TexCoordAddrType[] = {
	GL_TEXTURE_WRAP_S,	// TEXADDRTYPE_S,
	GL_TEXTURE_WRAP_T,	// TEXADDRTYPE_T,
};
#endif

//-----------------------------------------------------------------
#ifdef TARGET_D3D

#elif defined(TARGET_OPENGL)
static u_int	_locmap_TexCoordAddrMode[] = {
	GL_CLAMP,	// TEXADDRMODE_CLAMP,
	GL_REPEAT,	// TEXADDRMODE_WRAP,
};
#endif

//==================================================================
///
//==================================================================
void RendDeviceState::Reset()
{
	_zwrite = true;
	_ztest = true;

	_blend = false;
	_blend_src = PSYSGFX::BLENDFCT_SRC_ALPHA;
	_blend_dst = PSYSGFX::BLENDFCT_ONE_MINUS_SRC_ALPHA;

	_alphatst = false;
	_alphatst_func = PSYSGFX::TESTFUNC_ALWAYS;
	_alphatst_ref = 0.0f;

	_culling = PSYSGFX::CULL_OFF;
	_texturep = NULL;

#ifdef TARGET_D3D
	D3DDevice	*d3ddevp = _devicep->GetD3DDevice();

	d3ddevp->SetRenderState( D3DRS_ZWRITEENABLE, _zwrite );
	d3ddevp->SetRenderState( D3DRS_ZENABLE, _ztest );

	d3ddevp->SetRenderState( D3DRS_ALPHABLENDENABLE, _blend );
	d3ddevp->SetRenderState( D3DRS_SRCBLEND,	_locmap_BlendFactor[ _blend_src ] );
	d3ddevp->SetRenderState( D3DRS_DESTBLEND,	_locmap_BlendFactor[ _blend_dst ] );

	d3ddevp->SetRenderState( D3DRS_ALPHATESTENABLE, _alphatst );
	d3ddevp->SetRenderState( D3DRS_ALPHAFUNC, _locmap_TestFunc[_alphatst_func] );
	d3ddevp->SetRenderState( D3DRS_ALPHAREF, (int)(255*_alphatst_ref) );

	d3ddevp->SetRenderState( D3DRS_CULLMODE, _locmap_Culling[_culling] );
	
	if NOT( _texturep )
		d3ddevp->SetTexture( 0, NULL );

#elif defined(TARGET_OPENGL)
	glDepthMask( _zwrite ? 1 : 0 );

	if ( _ztest )	glEnable( GL_DEPTH_TEST );
	else			glDisable( GL_DEPTH_TEST );

	if ( _blend )	glEnable( GL_BLEND );
	else			glDisable( GL_BLEND );

	if ( _alphatst )glEnable( GL_ALPHA_TEST );
	else			glDisable( GL_ALPHA_TEST );
	glAlphaFunc( _locmap_TestFunc[_alphatst_func], _alphatst_ref );

	switch (_culling)
	{
	case PSYSGFX::CULL_CW:	glCullFace( GL_BACK );		break;
	case PSYSGFX::CULL_CCW:	glCullFace( GL_FRONT );		break;
	case PSYSGFX::CULL_OFF:	glDisable( GL_CULL_FACE );	break;
	}

	if NOT( _texturep )
		glDisable( GL_TEXTURE_2D );
	else
		glEnable( GL_TEXTURE_2D );

#endif
}

//==================================================================
void RendDeviceState::SetZWrite( bool onoff )
{
	if ( _zwrite == onoff )
		return;
	_zwrite = onoff;

#ifdef TARGET_D3D
	D3DDevice	*d3ddevp = _devicep->GetD3DDevice();
	d3ddevp->SetRenderState( D3DRS_ZWRITEENABLE, _zwrite );
#elif defined(TARGET_OPENGL)
	glDepthMask( _zwrite ? 1 : 0 );
#endif
}

//==================================================================
void RendDeviceState::SetZTest( bool onoff )
{
	if ( _ztest == onoff )
		return;
	_ztest = onoff;

#ifdef TARGET_D3D
	D3DDevice	*d3ddevp = _devicep->GetD3DDevice();
	d3ddevp->SetRenderState( D3DRS_ZENABLE, _ztest );
#elif defined(TARGET_OPENGL)
	if ( _ztest )	glEnable( GL_DEPTH_TEST );
	else			glDisable( GL_DEPTH_TEST );
#endif
}

//==================================================================
void RendDeviceState::SetCulling( PSYSGFX::Culling culling )
{
	if ( _culling == culling )
		return;
	_culling = culling;

#ifdef TARGET_D3D
	D3DDevice	*d3ddevp = _devicep->GetD3DDevice();
	switch ( _culling )
	{
	case PSYSGFX::CULL_CW:	d3ddevp->SetRenderState( D3DRS_CULLMODE, D3DCULL_CW );		break;
	case PSYSGFX::CULL_CCW:	d3ddevp->SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW );		break;
	case PSYSGFX::CULL_OFF:	d3ddevp->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );	break;
	}
#elif defined(TARGET_OPENGL)
	switch ( _culling )
	{
	case PSYSGFX::CULL_CW:	glCullFace( GL_BACK );		break;
	case PSYSGFX::CULL_CCW:	glCullFace( GL_FRONT );		break;
	case PSYSGFX::CULL_OFF:	glDisable( GL_CULL_FACE );	break;
	}
#endif
}

//==================================================================
void RendDeviceState::SetTexture( TextureBase *texturep )
{
	if ( _texturep == texturep )
		return;
	_texturep = texturep;

#ifdef TARGET_D3D
	if ( _texturep )
	{
		_devicep->GetD3DDevice()->SetTexture( _texturep->_d3dtex_cp, NULL );
	}
	else
	{
		_devicep->GetD3DDevice()->SetTexture( 0, NULL );
	}

#elif defined(TARGET_OPENGL)
	if ( _texturep )
	{
		glEnable( GL_TEXTURE_2D );
		glBindTexture( GL_TEXTURE_2D, _texturep->_tex_ogl_id );
	}
	else
	{
		glDisable( GL_TEXTURE_2D );
	}
#endif
}

//==================================================================
void RendDeviceState::SetBlending( bool onoff )
{
	if ( _blend == onoff )
		return;
	_blend = onoff;

#ifdef TARGET_D3D
	D3DDevice	*d3ddevp = _devicep->GetD3DDevice();
	d3ddevp->SetRenderState( D3DRS_ALPHABLENDENABLE, _blend );
#elif defined(TARGET_OPENGL)
	if ( _blend )	glEnable( GL_BLEND );
	else			glDisable( GL_BLEND );
#endif
}

//==================================================================
void RendDeviceState::SetBlending( bool onoff, PSYSGFX::BlendFactor src, PSYSGFX::BlendFactor dst )
{
	if ( _blend == onoff && !(onoff && (_blend_src != src || _blend_dst != dst) ) )
		return;
	_blend = onoff;

#ifdef TARGET_D3D
	D3DDevice	*d3ddevp = _devicep->GetD3DDevice();
	d3ddevp->SetRenderState( D3DRS_ALPHABLENDENABLE, _blend );
#elif defined(TARGET_OPENGL)
	if ( _blend )	glEnable( GL_BLEND );
	else			glDisable( GL_BLEND );
#endif

	if ( _blend )
	{
		_blend_src = src;
		_blend_dst = dst;

	#ifdef TARGET_D3D
		d3ddevp->SetRenderState( D3DRS_SRCBLEND,	_locmap_BlendFactor[ _blend_src ] );
		d3ddevp->SetRenderState( D3DRS_DESTBLEND,	_locmap_BlendFactor[ _blend_dst ] );
	#elif defined(TARGET_OPENGL)
		glBlendFunc( _locmap_BlendFactor[ _blend_src ], _locmap_BlendFactor[ _blend_dst ] );
	#endif
	}
}

//==================================================================
void RendDeviceState::SetAlphaTest( bool onoff, PSYSGFX::TestFunc tstfunc, float refparam )
{
	if ( _alphatst == onoff && _alphatst_func == tstfunc && _alphatst_ref == refparam )
		return;
	_alphatst = onoff;
	_alphatst_func = tstfunc;
	_alphatst_ref = refparam;

#ifdef TARGET_D3D
	D3DDevice	*d3ddevp = _devicep->GetD3DDevice();
	d3ddevp->SetRenderState( D3DRS_ALPHATESTENABLE, _alphatst );
	d3ddevp->SetRenderState( D3DRS_ALPHAFUNC, _locmap_TestFunc[_alphatst_func] );
	d3ddevp->SetRenderState( D3DRS_ALPHAREF, (int)(255*_alphatst_ref) );
#elif defined(TARGET_OPENGL)
	if ( _alphatst )glEnable( GL_ALPHA_TEST );
	else			glDisable( GL_ALPHA_TEST );
	glAlphaFunc( _locmap_TestFunc[_alphatst_func], _alphatst_ref );
#endif
}

//==================================================================
void RendDeviceState::SetAlphaTest( bool onoff )
{
//	if ( _alphatst == onoff )
//		return;
	_alphatst = onoff;

#ifdef TARGET_D3D
	D3DDevice	*d3ddevp = _devicep->GetD3DDevice();
	d3ddevp->SetRenderState( D3DRS_ALPHATESTENABLE, _alphatst );
#elif defined(TARGET_OPENGL)
	if ( _alphatst )glEnable( GL_ALPHA_TEST );
	else			glDisable( GL_ALPHA_TEST );
#endif
}

//==================================================================
void RendDeviceState::SetTextureFilter( int tex_idx, PSYSGFX::TexFilterType type, PSYSGFX::TexFilterMode mode )
{
#ifdef TARGET_D3D
	D3DDevice	*d3ddevp = _devicep->GetD3DDevice();
	d3ddevp->SetSamplerState( tex_idx, _locmap_TexFilterType[type], _locmap_TexFilterMode[mode] );
#elif defined(TARGET_OPENGL)
	glTexParameteri( GL_TEXTURE_2D, _locmap_TexFilterType[type], _locmap_TexFilterMode[mode] );
#endif
}

//==================================================================
void RendDeviceState::SetTextureCoordAddr( int tex_idx, PSYSGFX::TexAddrType type, PSYSGFX::TexAddrMode mode )
{
#ifdef TARGET_D3D
	D3DDevice	*d3ddevp = _devicep->GetD3DDevice();
	d3ddevp->SetSamplerState( tex_idx, _locmap_TexCoordAddrType[type], _locmap_TexCoordAddrMode[mode] );
#elif defined(TARGET_OPENGL)
	glTexParameteri( GL_TEXTURE_2D, _locmap_TexCoordAddrType[type], _locmap_TexCoordAddrMode[mode] );
#endif
}

/*
//==================================================================
void RendDeviceState::SetTextureWrap( PSYSGFX::TexFilterType type, PSYSGFX::TexFilterMode mode )
{
#ifdef TARGET_D3D
	D3DDevice	*d3ddevp = _devicep->GetD3DDevice();
	d3ddevp->SetSamplerState( _locmap_TexFilterType[type], _locmap_TexFilterMode[mode] );
#elif defined(TARGET_OPENGL)
	glTexParameteri( GL_TEXTURE_2D, _locmap_TexFilterType[type], _locmap_TexFilterMode[mode] );
#endif
}
*/
//==================================================================
void RendDeviceState::SetViewport( int x, int y, int w, int h, float min_z, float max_z )
{
	PASSERT( min_z >= 0 && min_z <= 1 );
	PASSERT( max_z >= 0 && max_z <= 1 );

	_viewport_xywh[0] = x;
	_viewport_xywh[1] = y;
	_viewport_xywh[2] = w;
	_viewport_xywh[3] = h;

	_viewport_minmaxz[0] = min_z;
	_viewport_minmaxz[1] = max_z;

#ifdef TARGET_D3D
	D3DVIEWPORT9	vport;

	vport.X = x;
	vport.Y = y;
	vport.Width = w;
	vport.Height = h;
	vport.MinZ = min_z;
	vport.MaxZ = max_z;

	D3DDevice	*d3ddevp = _devicep->GetD3DDevice();
	d3ddevp->SetViewport( &vport );

#elif defined(TARGET_OPENGL)

	glViewport( x, y, w, h );

#endif
}
